home *** CD-ROM | disk | FTP | other *** search
/ Languguage OS 2 / Languguage OS II Version 10-94 (Knowledge Media)(1994).ISO / gnu / dirent.lha / dirent / seekdir.c < prev    next >
C/C++ Source or Header  |  1988-09-02  |  3KB  |  116 lines

  1. /*
  2.     seekdir -- reposition a directory stream
  3.  
  4.     last edit:    25-Apr-1987    D A Gwyn
  5.  
  6.     An unsuccessful seekdir() will in general alter the current
  7.     directory position; beware.
  8.  
  9.     NOTE:    4.nBSD directory compaction makes seekdir() & telldir()
  10.         practically impossible to do right.  Avoid using them!
  11. */
  12.  
  13. #ifdef unos
  14. #include    <errno.h>
  15. #else
  16. #include    <sys/errno.h>
  17. #endif
  18. #include    <sys/types.h>
  19. #include    <dirent.h>
  20.  
  21. extern off_t    lseek();
  22.  
  23. extern int    errno;
  24.  
  25. #ifndef NULL
  26. #define    NULL    0
  27. #endif
  28.  
  29. #ifndef SEEK_SET
  30. #define    SEEK_SET    0
  31. #endif
  32.  
  33. typedef int    bool;            /* Boolean data type */
  34. #define    false    0
  35. #define    true    1
  36.  
  37. void
  38. seekdir( dirp, loc )
  39.     register DIR        *dirp;    /* stream from opendir() */
  40.     register off_t        loc;    /* position from telldir() */
  41. {
  42.     register bool        rewind;    /* "start over when stymied" flag */
  43.  
  44.     if ( dirp == NULL || dirp->dd_buf == NULL )
  45.         {
  46.         errno = EFAULT;
  47.         return;            /* invalid pointer */
  48.         }
  49.  
  50.     /* A (struct dirent)'s d_off is an invented quantity on 4.nBSD
  51.        NFS-supporting systems, so it is not safe to lseek() to it. */
  52.  
  53.     /* Monotonicity of d_off is heavily exploited in the following. */
  54.  
  55.     /* This algorithm is tuned for modest directory sizes.  For
  56.        huge directories, it might be more efficient to read blocks
  57.        until the first d_off is too large, then back up one block,
  58.        or even to use binary search on the directory blocks.  I
  59.        doubt that the extra code for that would be worthwhile. */
  60.  
  61.     if ( dirp->dd_loc >= dirp->dd_size    /* invalid index */
  62.       || ((struct dirent *)&dirp->dd_buf[dirp->dd_loc])->d_off > loc
  63.                     /* too far along in buffer */
  64.        )
  65.         dirp->dd_loc = 0;    /* reset to beginning of buffer */
  66.     /* else save time by starting at current dirp->dd_loc */
  67.  
  68.     for ( rewind = true; ; )
  69.         {
  70.         register struct dirent    *dp;
  71.  
  72.         /* See whether the matching entry is in the current buffer. */
  73.  
  74.         if ( (dirp->dd_loc < dirp->dd_size    /* valid index */
  75.            || readdir( dirp ) != NULL    /* next buffer read */
  76.            && (dirp->dd_loc = 0, true)    /* beginning of buffer set */
  77.              )
  78.           && (dp = (struct dirent *)&dirp->dd_buf[dirp->dd_loc])->d_off
  79.             <= loc        /* match possible in this buffer */
  80.            )    {
  81.             for ( /* dp initialized above */ ;
  82.                   (char *)dp < &dirp->dd_buf[dirp->dd_size];
  83.                   dp = (struct dirent *)((char *)dp + dp->d_reclen)
  84.                 )
  85.                 if ( dp->d_off == loc )
  86.                     {    /* found it! */
  87.                     dirp->dd_loc =
  88.                         (char *)dp - dirp->dd_buf;
  89.                     return;
  90.                     }
  91.  
  92.             rewind = false;    /* no point in backing up later */
  93.             dirp->dd_loc = dirp->dd_size;    /* set end of buffer */
  94.             }
  95.         else            /* whole buffer past matching entry */
  96.             if ( !rewind )
  97.                 {    /* no point in searching further */
  98.                 errno = EINVAL;
  99.                 return;    /* no entry at specified loc */
  100.                 }
  101.             else    {    /* rewind directory and start over */
  102.                 rewind = false;    /* but only once! */
  103.  
  104.                 dirp->dd_loc = dirp->dd_size = 0;
  105.  
  106.                 if ( lseek( dirp->dd_fd, (off_t)0, SEEK_SET )
  107.                     != 0
  108.                    )
  109.                     return;    /* errno already set (EBADF) */
  110.  
  111.                 if ( loc == 0 )
  112.                     return; /* save time for rewinddir() */
  113.                 }
  114.         }
  115. }
  116.